

//深拷贝练习


//可遍历类型
const mapTag='Map'
const setTag='Set'
const arrTag='Array'
const objTag='Object'
const deepTag=[
  mapTag,
  setTag,
  arrTag,
  objTag
]

//不可遍历类型
const numTag='Number'
const strTag='String'
const booTag='Boolean'
const symTag='Symbol'
const regTag='RegExp'
const dateTag='Date'
const errTag='Error'
const funTag='Function'

function getType(target){
  return Object.prototype.toString.call(target).slice(8,-1)
}

function isObject(target){
  return (typeof target==='object'&&target!==null)||typeof target ==='function'
}

function cloneSym(target){
  return Object(Symbol.prototype.valueOf.call(target))
}

function cloneReg(target){
  const reFlags=/\w+&/
  const res=new target.constructor(target.source,reFlags.exec(target))
  res.lastIndex=target.lastIndex
  return res
}

function cloneFun(target){
  const bodyReg=/(?<=\{)(.|\n)+(?=\})/
  const paramReg=/(?<=\().+(?=\)\s+)/
  const funStr=target.toString()
  const param=paramReg.exec(funStr)
  const paramArr=param?param[0].split(','):[]
  if(target.prototype){
    const body=bodyReg.exec(funStr)
    if(body){
      return new Function(...paramArr,body[0])
    }else{
      return new Function(...paramArr)
    }
  }else{
    return eval(funStr)
  }
}

function cloneOtherType(target,type){
  const maker=target.constructor
  switch(type){
    case numTag:
    case strTag:
    case booTag:
    case errTag:
    case dateTag:
      return new maker(target)
    case symTag:
      return cloneSym(target)
    case regTag:
      return cloneReg(target)
    case funTag:
      return cloneFun(target)
    default:
      return null;
  }
}

function deepClone(data,map=new Map()){
  //原始数据返回
  if(!isObject(dat))return data

  //初始化
  const type=getType(data)
  let target
  if(deepTag.includes(type)){
    target=new data.constructor()
  }

  //map避免循环引用
  if(map.has(data))return map.get(data)
  map.set(data,target)

  //克隆set
  if(type===setTag){
    data.forEach(val=>target.add(val))
    return target
  }

  //克隆map
  if(type === mapTag){
    data.forEach((val,key)=>target.set(key,val))
    return target
  }

  //克隆普通对象或数组
  if(type===objTag||type===arrTag){
    const keys=type===objTag?Object.keys(target):null
    for (let i = 0; i < keys?keys.length:data.length; i++) {
      const key=keys?keys[i]:i
      if(isObject(data[key])){
        target[key]=deepClone(data[key])
      }else{
        target[key]=data[key]
      }
    }
    return target
  }

  //克隆其它类型
  return cloneOtherType(data,type)
}